#pragma once


#include "Containers/Map.h"
#include "Containers/Any.h"
#include "Type.h"
#include "FunctionID.h"
#include "Reflection.h"
#include "DecoratedType.h"

class CFunctionList;
class CFunctionTypeGeneralized;

class IFunctor
{
public:

    virtual CAny Invoke(const TVector<CAny>& InArgs) = 0;

    virtual ~IFunctor() = default;
};

class CCPPFunctionBody : public IFunctor
{
public:
    CCPPFunctionBody(std::function<CAny(const TVector<CAny>&)> InFunction)
        :Function(InFunction)
    {

    }

    CAny Invoke(const TVector<CAny>& InArgs) override;

protected:
    std::function<CAny( const TVector<CAny>&)> Function;
};


class WHENGINE_API CFunction 
{
public:
    CFunction(
        CFunctionList* InParentClass
        , const CString& InName
        , const SDecoratedType& InReturnType
        , const TVector<SDecoratedType>& InParameterTypes
        , TMap<EFunctionFlags, SReflectionMetaDataValue> InMetaData
        );

    SFunctionID ID;

    CString Name;

    CType* GetOwnerType();

    static CString GetConstructorName();

    SDecoratedType ReturnType=nullptr;

    TVector<SDecoratedType> ParameterTypes;

    virtual SDecoratedType GetReturnType() {return ReturnType;};

    virtual TVector<SDecoratedType> GetParameterTypes() {return ParameterTypes;};

    virtual uint32_t GetParameterNum() {return ParameterTypes.Num();}

    //is a cpp function?
    virtual bool IsNative() {return false;}

    void AddParameter(SDecoratedType InType)
    {
        ParameterTypes.Add(InType);
    }

    CFunctionTypeGeneralized* GetFunctionType()
    {
        return FunctionType;
    }

    void SetFunctionType(CFunctionTypeGeneralized* InFunctionType)
    {
        FunctionType=InFunctionType;
    }

    //virtual TVector<CString> GetParameterNames() {return TVector<CString>();};
    void SetStatic()
    {
        bIsStatic=true;
    }

    bool bIsStatic=false;

    std::unique_ptr<IFunctor> Functor;

    void SetCppBody(const std::function<CAny(const TVector<CAny>&)> & InFunction);

    CAny Invoke(const TVector<CAny>& InArgs) ;

    CString GetName() {return Name;}

    bool IsStatic() {return bIsStatic;}

    bool IsVirtual() {return bIsVirtual;}
    void MarkAsVirtual();

    bool IsConstructor();

    bool IsTypeSupportScript() 
    {
        return FunctionType->CanUseInScript();
    }

    bool CanUseInScript() ;
protected:
    bool bIsVirtual=false;

    CFunctionList* OwnerClass=nullptr;

    CFunctionTypeGeneralized* FunctionType=nullptr;

    TMap<EFunctionFlags, SReflectionMetaDataValue> MetaData;
};